Análisis de los factores que influyen en la decisión de compra de vivienda a nivel internacional

2025-11-06

Ander Castro & Lucas Martinez

1. Introducción

La compra de vivienda es uno de los procesos financieros más importantes para los individuos y familias en todo el mundo. Para la mayoría de compradores, esta adquisición se realiza a través de un préstamo hipotecario, por lo que las instituciones financieras evalúan múltiples factores para determinar si un solicitante es elegible para un crédito y bajo qué condiciones.

El mercado inmobiliario global es un sector clave en la economía, moviendo miles de millones de dólares anualmente. La correcta evaluación del perfil financiero del comprador, junto con las características del inmueble, es fundamental tanto para garantizar el acceso a la vivienda como para reducir el riesgo económico de las entidades financieras.

1.1 Obtención de los datos

El set de datos fue obtenido de la plataforma Kaggle y contiene información global sobre compras de vivienda y decisiones de aprobación hipotecaria. Incluye 200,000 registros (muestras) con 25 variables relacionadas tanto con la propiedad como con el comprador y su historial financiero.

El objetivo principal de este estudio es realizar un análisis estadístico para comprender qué factores influyen en la aprobación o rechazo de un préstamo hipotecario. Asimismo, se buscará identificar qué características del comprador y de la propiedad tienen mayor peso en la decisión final y desarrollar modelos predictivos que permitan anticipar el resultado de aprobación crediticia.

1.2 Metodología

A lo largo de este trabajo, se pretenden analizar las siguientes preguntas:

El análisis se llevará a cabo de la siguiente manera:

  1. Importación de librerías y datos
  2. Resumen estadístico general
  3. Visualización de datos
  4. Contraste de hipótesis y estadística inferencial
  5. PCA: Análisis de Componentes Principales
    • Varianza explicada
    • Componentes principales
  6. Modelos de Machine Learning para la decisión de compra
    • Regresión Logística
    • Random Forest
  7. Interpretación de resultados
  8. Conclusiones y limitaciones
  9. Bibliografía

1.3 Variables del dataset

El conjunto de datos Global House Purchase Decision Dataset contiene información detallada sobre propiedades residenciales, características del entorno y perfil financiero de los compradores. En total, incluye 200.000 observaciones y 25 variables, que permiten analizar los factores que influyen en la decisión de compra.

A continuación, se describen las variables incluidas en el dataset:

2. Análisis exploratorio de los datos

2.1 Importar datos y librerías

library(dplyr)
library(ggplot2)
library(tidyr)
library(corrplot)
library(scales)
library(broom)
library(car)
library(caret)
library(xgboost)
library(glmnet)
library(pROC)

data <- read.csv('./global_house_purchase_dataset.csv')
glimpse(data)
## Rows: 200,000
## Columns: 25
## $ property_id             <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,…
## $ country                 <chr> "France", "South Africa", "South Africa", "Ger…
## $ city                    <chr> "Marseille", "Cape Town", "Johannesburg", "Fra…
## $ property_type           <chr> "Farmhouse", "Apartment", "Farmhouse", "Farmho…
## $ furnishing_status       <chr> "Semi-Furnished", "Semi-Furnished", "Semi-Furn…
## $ property_size_sqft      <int> 991, 1244, 4152, 3714, 531, 3169, 1986, 4048, …
## $ price                   <int> 412935, 224538, 745104, 1110959, 99041, 110736…
## $ constructed_year        <int> 1989, 1990, 2019, 2008, 2007, 1985, 1976, 2020…
## $ previous_owners         <int> 6, 4, 5, 1, 6, 0, 1, 4, 6, 2, 4, 2, 4, 6, 6, 1…
## $ rooms                   <int> 6, 8, 2, 3, 3, 5, 2, 6, 2, 5, 8, 3, 2, 7, 3, 8…
## $ bathrooms               <int> 2, 8, 1, 3, 3, 2, 1, 6, 1, 2, 3, 1, 2, 2, 1, 6…
## $ garage                  <int> 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0…
## $ garden                  <int> 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1…
## $ crime_cases_reported    <int> 1, 1, 0, 0, 3, 0, 0, 1, 0, 0, 1, 4, 2, 6, 0, 1…
## $ legal_cases_on_property <int> 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1…
## $ customer_salary         <int> 10745, 16970, 21914, 17980, 17676, 95520, 1142…
## $ loan_amount             <int> 193949, 181465, 307953, 674720, 65833, 793316,…
## $ loan_tenure_years       <int> 15, 20, 30, 15, 25, 30, 25, 20, 15, 10, 25, 20…
## $ monthly_expenses        <int> 6545, 8605, 2510, 8805, 8965, 10615, 14440, 72…
## $ down_payment            <int> 218986, 43073, 437151, 436239, 33208, 314052, …
## $ emi_to_income_ratio     <dbl> 0.16, 0.08, 0.09, 0.33, 0.03, 0.05, 0.16, 0.13…
## $ satisfaction_score      <int> 1, 9, 6, 2, 3, 10, 9, 1, 8, 1, 8, 2, 3, 6, 1, …
## $ neighbourhood_rating    <int> 5, 1, 8, 6, 3, 8, 10, 5, 8, 4, 7, 5, 7, 9, 2, …
## $ connectivity_score      <int> 6, 2, 1, 6, 4, 2, 10, 8, 10, 7, 6, 6, 2, 1, 7,…
## $ decision                <int> 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0…
sum(is.na(data))
## [1] 0
sum(duplicated(data))
## [1] 0

2.2 Resumen estadístico general

data %>%
  select_if(is.numeric) %>%
  summary()
##   property_id     property_size_sqft     price         constructed_year
##  Min.   :     1   Min.   : 400       Min.   :  56288   Min.   :1960    
##  1st Qu.: 50001   1st Qu.:1802       1st Qu.: 565990   1st Qu.:1975    
##  Median :100001   Median :3190       Median :1023429   Median :1991    
##  Mean   :100001   Mean   :3196       Mean   :1215365   Mean   :1991    
##  3rd Qu.:150000   3rd Qu.:4589       3rd Qu.:1725556   3rd Qu.:2008    
##  Max.   :200000   Max.   :6000       Max.   :4202732   Max.   :2023    
##  previous_owners     rooms         bathrooms        garage      
##  Min.   :0.000   Min.   :1.000   Min.   :1.00   Min.   :0.0000  
##  1st Qu.:1.000   1st Qu.:3.000   1st Qu.:1.00   1st Qu.:0.0000  
##  Median :3.000   Median :5.000   Median :2.00   Median :0.0000  
##  Mean   :3.001   Mean   :4.514   Mean   :2.76   Mean   :0.4994  
##  3rd Qu.:5.000   3rd Qu.:7.000   3rd Qu.:4.00   3rd Qu.:1.0000  
##  Max.   :6.000   Max.   :8.000   Max.   :8.00   Max.   :1.0000  
##      garden       crime_cases_reported legal_cases_on_property customer_salary 
##  Min.   :0.0000   Min.   : 0.000       Min.   :0.0000          Min.   :  2000  
##  1st Qu.:0.0000   1st Qu.: 0.000       1st Qu.:0.0000          1st Qu.: 21450  
##  Median :1.0000   Median : 1.000       Median :0.0000          Median : 41465  
##  Mean   :0.5002   Mean   : 1.229       Mean   :0.2489          Mean   : 46529  
##  3rd Qu.:1.0000   3rd Qu.: 2.000       3rd Qu.:0.0000          3rd Qu.: 70805  
##  Max.   :1.0000   Max.   :10.000       Max.   :1.0000          Max.   :100000  
##   loan_amount      loan_tenure_years monthly_expenses  down_payment    
##  Min.   :  23504   Min.   :10.00     Min.   :  500    Min.   :   8966  
##  1st Qu.: 337280   1st Qu.:15.00     1st Qu.: 5770    1st Qu.: 184959  
##  Median : 626932   Median :20.00     Median :10520    Median : 356170  
##  Mean   : 759758   Mean   :19.99     Mean   :10560    Mean   : 455607  
##  3rd Qu.:1058416   3rd Qu.:25.00     3rd Qu.:15260    3rd Qu.: 625735  
##  Max.   :3520150   Max.   :30.00     Max.   :20000    Max.   :2492723  
##  emi_to_income_ratio satisfaction_score neighbourhood_rating connectivity_score
##  Min.   :0.0000      Min.   : 1.000     Min.   : 1.000       Min.   : 1.000    
##  1st Qu.:0.0700      1st Qu.: 3.000     1st Qu.: 3.000       1st Qu.: 3.000    
##  Median :0.1300      Median : 5.000     Median : 5.000       Median : 5.000    
##  Mean   :0.1954      Mean   : 5.499     Mean   : 5.505       Mean   : 5.496    
##  3rd Qu.:0.2400      3rd Qu.: 8.000     3rd Qu.: 8.000       3rd Qu.: 8.000    
##  Max.   :3.4600      Max.   :10.000     Max.   :10.000       Max.   :10.000    
##     decision     
##  Min.   :0.0000  
##  1st Qu.:0.0000  
##  Median :0.0000  
##  Mean   :0.2303  
##  3rd Qu.:0.0000  
##  Max.   :1.0000
sapply(data %>% select(where(is.character)), n_distinct)
##           country              city     property_type furnishing_status 
##                13                40                 6                 3

2.3 Visualización de datos

Distribución de la variable decision

data %>%
  count(decision) %>%
  mutate(pct = n/sum(n)*100,
         decision = factor(decision, labels = c("No compra", "Compra"))) %>%
  ggplot(aes(x = decision, y = pct, fill = decision)) +
  geom_col() +
  geom_text(aes(label = sprintf("%.2f%%", pct)), 
            vjust = 1.5, color = "black", size = 4) +
  labs(title = "Distribución de la decisión de compra",
       x = "Decisión", y = "%") +
  scale_fill_manual(values = c("#F8766D", "#00BA38")) +
  theme_minimal() +
  theme(legend.position = "none")

El gráfico representa la distribución porcentual de la variable decision, que indica si un cliente decide o no comprar una vivienda. Se observa un desequilibrio marcado entre ambas categorías: el 76.97% de los individuos no realiza la compra, mientras que solo el 23.03% decide concretarla.

Esta distribución desequilibrada es un aspecto importante a considerar, ya que puede influir en el rendimiento de los modelos predictivos.

Distribución del precio

ggplot(data, aes(x = price)) +
  geom_histogram(fill = "#69b3a2", color = "white", bins = 40) +
  scale_x_continuous(labels = comma) +
  labs(title = "Distribución del precio de las viviendas",
  x = "Precio (USD)", y = "Frecuencia") +
  theme_minimal()

El histograma muestra la distribución del precio de las viviendas en dólares estadounidenses (USD). Se observa una distribución asimétrica a la derecha, parecida a una distribución gamma, lo que indica que la mayoría de los inmuebles se concentran en los rangos de precios más bajos, mientras que un número reducido de propiedades presenta precios considerablemente más altos.

La mayor frecuencia de viviendas se sitúa aproximadamente entre los 500,000 y 1,000,000 USD, reflejando que este rango constituye el segmento más representativo del mercado analizado. A partir de valores superiores a los 2 millones de dólares, la frecuencia disminuye progresivamente, mostrando la presencia de propiedades de lujo que elevan la cola derecha de la distribución.

A continuación, aplicaremos una transformación logarítmica al precio con el objetivo de aproximar su distribución a una normal.

data <- data %>% mutate(log_price = log(price))

ggplot(data, aes(x = log_price)) +
  geom_histogram(fill = "#69b3a2", color = "white", bins = 40) +
  scale_x_continuous() +
  labs(title = "Distribución del logaritmo del precio de las viviendas",
  x = "Precio (USD)", y = "Frecuencia") +
  theme_minimal()

Tras aplicar la transformación logarítmica al precio de las viviendas, se observa que la distribución se aproxima más a una distribución normal, corrigiendo la fuerte asimetría a la derecha presente en los valores originales.

Distribución del precio por país y por tipo de propiedad

# Boxplot de precios por país
data %>%
  ggplot(aes(x = country, y = price)) +
  geom_boxplot(fill = "#56B4E9", outlier.alpha = 0.2) +
  coord_flip() +
  scale_y_continuous(labels = comma) +
  labs(title = "Distribución del precio por país (Top 10)",
       x = "País", y = "Precio (USD)") +
  theme_minimal()

En este gráfico se aprecia que Singapur, Emiratos Árabes Unidos (UAE), Japón y Estados Unidos presentan los precios medianos más altos, así como una mayor dispersión, lo que refleja mercados con viviendas de alto valor y mayor desigualdad de precios. En contraste, países como Brasil, India y Sudáfrica muestran precios sustancialmente más bajos y con menor variabilidad, indicando mercados más accesibles y homogéneos.

# Boxplot de precios por tipo de propiedad

data %>%
  ggplot(aes(x = property_type, y = price)) +
  geom_boxplot(fill = "#56B4E9", outlier.alpha = 0.2) +
  scale_y_continuous(labels = comma) +
  coord_flip() +
  labs(title = "Distribución del precio por tipo de propiedad (Top 10)",
       x = "Propiedad", y = "Precio (USD)") +
  theme_minimal()

Por otro lado, el boxplot por tipo de propiedad evidencia que no hay diferencias estructurales dentro de cada mercado. Si bien es cierto que hay propiedades muy caras (outliers) en todos los tipos de propiedades, las distribuciones centrales son bastante similares, sin diferencias estructurales marcadas entre categorías.

Relaciones entre variables numéricas

# Seleccionar variables numéricas relevantes

num_vars <- data %>%
  select_if(is.numeric) %>%
  select(-property_id)

# Matriz de correlación

corr_matrix <- cor(num_vars)

corrplot(corr_matrix, method = "color", type = "upper", tl.col = "black", order = "hclust")

El análisis de las relaciones entre las variables numéricas revela patrones coherentes con el comportamiento esperado en un contexto inmobiliario.

En primer lugar, se observa una correlación positiva entre el precio de la vivienda y el tamaño del inmueble (property_size_sqft), lo cual es lógico, ya que las propiedades de mayor superficie tienden a tener precios más altos.

Asimismo, variables como el salario del comprador (customer_salary), la cantidad inicial que el comprador aporta (down_payment) y el monto del préstamo (loan_amount) muestran también una relación directa con el precio, reflejando que los compradores con mayor capacidad económica tienden a adquirir viviendas más costosas.

En cuanto a correlaciones negativas, vemos una relación lógica entre customer_salary y emi_to_income_ratio: cuanto mayor es el salario del comprador, menor es la proporción que representa la cuota mensual del préstamo respecto a sus ingresos. Esto es coherente, ya que un ingreso alto diluye el peso relativo de la cuota hipotecaria, mejorando la capacidad de pago.

Además, existe una correlación negativa interesante entre legal_cases_on_property y decision. Esto significa que, a medida que aumentan los casos legales asociados a una propiedad, disminuye la probabilidad de que la decisión final sea positiva (compra aprobada).

Relaciones bivariantes

# Salario vs Decisión de compra
data %>%
  mutate(decision = factor(decision, labels = c("No compra", "Compra"))) %>%
  ggplot(aes(x = decision, y = customer_salary, fill = decision)) +
  geom_boxplot(outlier.alpha = 0.2) +
  scale_fill_manual(values = c("#F8766D", "#00BA38")) +
  labs(title = "Salario según decisión de compra",
  x = "Decisión", y = "Salario del cliente") +
  theme_minimal() +
  theme(legend.position = "none")

# Número de crímenes reportados vs Precio medio
data %>%
  group_by(crime_cases_reported) %>%
  summarise(mean_price = mean(price, na.rm = TRUE)) %>%
  ggplot(aes(x = factor(crime_cases_reported), y = mean_price)) +
  geom_col(fill = "#56B4E9") +
  scale_y_continuous(labels = comma) +
  labs(title = "Precio medio según el número de crímenes reportados",
  x = "Número de crímenes", y = "Precio medio (USD)") +
  theme_minimal()

El análisis de las relaciones bivariantes permite identificar cómo ciertas variables influyen directamente en la decisión de compra y en el precio de la vivienda.

En primer lugar, se observa que el salario del cliente (customer_salary) presenta una relación positiva con la decisión de compra.

Por otra parte, la relación entre el número de crímenes reportados (crime_cases_reported) y el precio medio de las viviendas muestra una tendencia inversa: los precios disminuyen a medida que aumenta la incidencia delictiva.

Decisión de compra por país

# ¿Qué porcentaje de personas toma la decisión de comprar una vivienda en cada país?
data %>%
  group_by(country) %>%
  summarise(pct_purchase = mean(decision) * 100) %>%
  arrange(desc(pct_purchase)) %>%
  slice_head(n = 10) %>%
  ggplot(aes(x = reorder(country, pct_purchase), y = pct_purchase)) +
  geom_col(fill = "#009E73") +
  geom_text(aes(label = sprintf("%.2f%%", pct_purchase)), 
            hjust = 1.2, color = "white", size = 4) +
  coord_flip() +
  labs(title = "Porcentaje de decisión de compra por país (Top 10)",
  x = "País", y = "% de compras") +
  theme_minimal()

El análisis de la decisión de compra por país revela diferencias significativas en el comportamiento de los compradores según su ubicación geográfica.

Se observa que algunos países presentan una mayor proporción de decisiones de compra positivas, lo que puede estar relacionado con factores como la estabilidad económica, el nivel de ingresos promedio o las características del mercado inmobiliario local.

3. Contraste de hipótesis y estadística inferencial

En esta sección se pretende verificar estadísticamente algunas de las relaciones sugeridas en el análisis exploratorio previo. Para ello, se formulan hipótesis estadísticas y se aplican pruebas de contraste adecuadas según el tipo de variable y la distribución observada.

3.1 Diferencias de precio entre países

Hipótesis:

# Test de normalidad
shapiro.test(sample(data$log_price, 5000))
## 
##  Shapiro-Wilk normality test
## 
## data:  sample(data$log_price, 5000)
## W = 0.97328, p-value < 2.2e-16

El test de Shapiro–Wilk, aplicado a una muestra aleatoria de la variable log_price, muestra un p-value < 2.2e-16, lo que sugiere que la variable transformada no sigue una distribución normal perfecta. No obstante, dado el gran tamaño muestral, el ANOVA resulta suficientemente robusto frente a desviaciones de normalidad, por lo que la ligera falta de ajuste no compromete la validez del análisis.

A continuación, se realiza la prueba de Levene para ver si las varianzas entre países son homogéneas o no, y esto nos indicará si podemos utilizar el test paramétrico de ANOVA o, en caso contrario, el de Kruskal-Wallis.

# Test de homogeneidad de varianzas
leveneTest(log_price ~ factor(country), data = data)
## Levene's Test for Homogeneity of Variance (center = median)
##           Df F value Pr(>F)
## group     12  1.4727  0.126
##       199987

El resultado (p = 0.126) sugiere que no existe evidencia suficiente para rechazar la hipótesis nula de igualdad de varianzas, cumpliéndose así el supuesto de homocedasticidad. Por ello, se aplicó un ANOVA clásico para comparar el precio medio entre países usando la variable transformada.

# ANOVA
anova_country <- aov(log_price ~ factor(country), data = data)
summary(anova_country)
##                     Df Sum Sq Mean Sq F value Pr(>F)    
## factor(country)     12  38869    3239    7414 <2e-16 ***
## Residuals       199987  87373       0                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

El ANOVA realizado sobre el precio de la vivienda en su escala logarítmica revela diferencias estadísticamente significativas entre países. El modelo muestra un p-value < 2e-16, lo que indica que la variación del precio medio de las viviendas difiere de forma sustancial según el país.

Ahora llevaremos a cabo el test de Tukey para ver qué países difieren entre sí en el precio medio. Este test incluye una corrección automática del error familiar (FWER), garantizando que la probabilidad de obtener falsos positivos se mantenga controlada al realizar múltiples comparaciones simultáneas.

# Post-hoc Tukey
tukey <- TukeyHSD(anova_country)
tukey
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = log_price ~ factor(country), data = data)
## 
## $`factor(country)`
##                               diff         lwr         upr p adj
## Brazil-Australia       -0.47616460 -0.50110219 -0.45122702 0e+00
## Canada-Australia        0.07412258  0.04918662  0.09905854 0e+00
## China-Australia         0.17005499  0.14517333  0.19493665 0e+00
## France-Australia        0.26375468  0.23890955  0.28859980 0e+00
## Germany-Australia      -0.07306215 -0.09799528 -0.04812902 0e+00
## India-Australia        -0.76989040 -0.79484424 -0.74493656 0e+00
## Japan-Australia         0.32866878  0.30369861  0.35363896 0e+00
## Singapore-Australia     0.77667016  0.75168399  0.80165633 0e+00
## South Africa-Australia -0.59123686 -0.61617282 -0.56630089 0e+00
## UAE-Australia           0.61147800  0.58643507  0.63652092 0e+00
## UK-Australia            0.21916180  0.19423070  0.24409290 0e+00
## USA-Australia           0.44443000  0.41944507  0.46941494 0e+00
## Canada-Brazil           0.55028718  0.52533303  0.57524134 0e+00
## China-Brazil            0.64621959  0.62131970  0.67111948 0e+00
## France-Brazil           0.73991928  0.71505590  0.76478266 0e+00
## Germany-Brazil          0.40310245  0.37815114  0.42805377 0e+00
## India-Brazil           -0.29372580 -0.31869781 -0.26875378 0e+00
## Japan-Brazil            0.80483339  0.77984505  0.82982172 0e+00
## Singapore-Brazil        1.25283476  1.22783044  1.27783908 0e+00
## South Africa-Brazil    -0.11507225 -0.14002640 -0.09011810 0e+00
## UAE-Brazil              1.08764260  1.06258156  1.11270363 0e+00
## UK-Brazil               0.69532640  0.67037711  0.72027570 0e+00
## USA-Brazil              0.92059461  0.89559152  0.94559769 0e+00
## China-Canada            0.09593241  0.07103414  0.12083067 0e+00
## France-Canada           0.18963210  0.16477034  0.21449385 0e+00
## Germany-Canada         -0.14718473 -0.17213443 -0.12223504 0e+00
## India-Canada           -0.84401298 -0.86898338 -0.81904259 0e+00
## Japan-Canada            0.25454620  0.22955949  0.27953292 0e+00
## Singapore-Canada        0.70254758  0.67754488  0.72755028 0e+00
## South Africa-Canada    -0.66535944 -0.69031197 -0.64040691 0e+00
## UAE-Canada              0.53735541  0.51229599  0.56241484 0e+00
## UK-Canada               0.14503922  0.12009154  0.16998689 0e+00
## USA-Canada              0.37030742  0.34530595  0.39530889 0e+00
## France-China            0.09369969  0.06889240  0.11850698 0e+00
## Germany-China          -0.24311714 -0.26801256 -0.21822171 0e+00
## India-China            -0.93994539 -0.96486156 -0.91502922 0e+00
## Japan-China             0.15861380  0.13368127  0.18354632 0e+00
## Singapore-China         0.60661517  0.58166663  0.63156372 0e+00
## South Africa-China     -0.76129184 -0.78619011 -0.73639358 0e+00
## UAE-China               0.44142301  0.41641762  0.46642840 0e+00
## UK-China                0.04910681  0.02421341  0.07400021 0e+00
## USA-China               0.27437502  0.24942770  0.29932233 0e+00
## Germany-France         -0.33681683 -0.36167574 -0.31195792 0e+00
## India-France           -1.03364508 -1.05852477 -1.00876539 0e+00
## Japan-France            0.06491411  0.04001804  0.08981017 0e+00
## Singapore-France        0.51291548  0.48800337  0.53782759 0e+00
## South Africa-France    -0.85499153 -0.87985329 -0.83012978 0e+00
## UAE-France              0.34772332  0.32275428  0.37269235 0e+00
## UK-France              -0.04459288 -0.06944976 -0.01973600 2e-07
## USA-France              0.18067533  0.15576445  0.20558620 0e+00
## India-Germany          -0.69682825 -0.72179582 -0.67186069 0e+00
## Japan-Germany           0.40173094  0.37674705  0.42671482 0e+00
## Singapore-Germany       0.84973231  0.82473244  0.87473218 0e+00
## South Africa-Germany   -0.51817470 -0.54312440 -0.49322501 0e+00
## UAE-Germany             0.68454015  0.65948355  0.70959675 0e+00
## UK-Germany              0.29222395  0.26727911  0.31716879 0e+00
## USA-Germany             0.51749216  0.49249351  0.54249080 0e+00
## Japan-India             1.09855919  1.07355463  1.12356375 0e+00
## Singapore-India         1.54656056  1.52154003  1.57158109 0e+00
## South Africa-India      0.17865355  0.15368315  0.20362394 0e+00
## UAE-India               1.38136840  1.35629118  1.40644561 0e+00
## UK-India                0.98905220  0.96408666  1.01401774 0e+00
## USA-India               1.21432041  1.18930110  1.23933971 0e+00
## Singapore-Japan         0.44800138  0.42296456  0.47303820 0e+00
## South Africa-Japan     -0.91990564 -0.94489236 -0.89491892 0e+00
## UAE-Japan               0.28280921  0.25771575  0.30790267 0e+00
## UK-Japan               -0.10950699 -0.13448885 -0.08452512 0e+00
## USA-Japan               0.11576122  0.09072563  0.14079681 0e+00
## South Africa-Singapore -1.36790702 -1.39290972 -1.34290431 0e+00
## UAE-Singapore          -0.16519216 -0.19030154 -0.14008278 0e+00
## UK-Singapore           -0.55750836 -0.58250622 -0.53251051 0e+00
## USA-Singapore          -0.33224016 -0.35729170 -0.30718861 0e+00
## UAE-South Africa        1.20271485  1.17765543  1.22777427 0e+00
## UK-South Africa         0.81039865  0.78545098  0.83534633 0e+00
## USA-South Africa        1.03566686  1.01066539  1.06066833 0e+00
## UK-UAE                 -0.39231620 -0.41737078 -0.36726161 0e+00
## USA-UAE                -0.16704799 -0.19215614 -0.14193984 0e+00
## USA-UK                  0.22526821  0.20027158  0.25026483 0e+00
sum(tukey[["factor(country)"]][, "p adj"]>0.05)
## [1] 0

El test post-hoc de Tukey mostró diferencias significativas entre todos los pares de países (p-value < 0.05), confirmando que el país de localización es un factor determinante en el precio de la vivienda.

3.2 Diferencia de precio entre casas con y sin jardín

Hipótesis:

t_test <- data%>%
  mutate(garden = factor(garden)) %>%
  select(price, garden)

shapiro.test(sample(t_test$price[t_test$garden == 0], 5000))
## 
##  Shapiro-Wilk normality test
## 
## data:  sample(t_test$price[t_test$garden == 0], 5000)
## W = 0.92619, p-value < 2.2e-16
shapiro.test(sample(t_test$price[t_test$garden == 1], 5000))
## 
##  Shapiro-Wilk normality test
## 
## data:  sample(t_test$price[t_test$garden == 1], 5000)
## W = 0.9267, p-value < 2.2e-16
wilcox <- wilcox.test(price ~ garden, data = data)
wilcox
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  price by garden
## W = 5000087495, p-value = 0.9945
## alternative hypothesis: true location shift is not equal to 0

El test de Shapiro-Wilk aplicado a los precios en los grupos con y sin jardín presentó p-values menores a 2.2e-16, lo que indica que en ambos casos la distribución del precio se desvía significativamente de la normalidad. Por esta razón, se utilizó el test no paramétrico de Wilcoxon para comparar los precios entre los dos grupos. El resultado del test (p = 0.9945) muestra que no existe evidencia estadísticamente significativa para afirmar que el precio de las propiedades difiera entre viviendas con jardín y viviendas sin jardín. En otras palabras, la presencia de jardín no parece estar asociada con diferencias en el nivel de precios dentro de este conjunto de datos.

3.3 Dependencia entre el tipo de propiedad y garaje

Hipótesis:

tabla_contingencia <- table(data$property_type, data$garage)
tabla_contingencia
##                    
##                         0     1
##   Apartment         16756 16642
##   Farmhouse         16950 16568
##   Independent House 16520 16814
##   Studio            16382 16626
##   Townhouse         16688 16707
##   Villa             16834 16513
# Test de Chi-cuadrado
chi_resultado <- chisq.test(tabla_contingencia)
chi_resultado
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_contingencia
## X-squared = 11.902, df = 5, p-value = 0.03615

Se realizó un test de Chi-cuadrado para evaluar la relación entre el tipo de propiedad y la presencia de garaje. El resultado obtenido (p = 0.036) indica que existe una diferencia estadísticamente significativa entre ambos factores, ya que el p-value es menor que 0.05. Esto implica que la disponibilidad de garaje no es independiente del tipo de vivienda: algunos tipos de propiedad presentan mayor probabilidad de contar con garaje que otros.

4. PCA: Análisis de Componentes Principales

El análisis de componentes principales (PCA) es clave para identificar los factores que explican la mayor parte de la variabilidad en nuestro conjunto de datos.

# PCA no admite factores ni variables categóricas
pca_data <- data %>%
  select(property_size_sqft, price, log_price,constructed_year,
         previous_owners, rooms, bathrooms, garage, garden,
         crime_cases_reported, legal_cases_on_property,
         customer_salary, loan_amount, loan_tenure_years,
         monthly_expenses, down_payment, emi_to_income_ratio,
         satisfaction_score, neighbourhood_rating,
         connectivity_score)

pca_result <- prcomp(pca_data, scale. = TRUE)
summary(pca_result)
## Importance of components:
##                           PC1     PC2     PC3    PC4     PC5     PC6     PC7
## Standard deviation     2.0942 1.27540 1.26397 1.0120 1.01148 1.00432 1.00271
## Proportion of Variance 0.2193 0.08133 0.07988 0.0512 0.05116 0.05043 0.05027
## Cumulative Proportion  0.2193 0.30061 0.38049 0.4317 0.48285 0.53328 0.58356
##                            PC8     PC9    PC10    PC11    PC12    PC13    PC14
## Standard deviation     1.00140 1.00018 0.99891 0.99848 0.99727 0.99482 0.93835
## Proportion of Variance 0.05014 0.05002 0.04989 0.04985 0.04973 0.04948 0.04403
## Cumulative Proportion  0.63370 0.68371 0.73360 0.78345 0.83318 0.88266 0.92669
##                           PC15    PC16    PC17    PC18   PC19      PC20
## Standard deviation     0.66991 0.61111 0.56839 0.47014 0.3161 1.081e-14
## Proportion of Variance 0.02244 0.01867 0.01615 0.01105 0.0050 0.000e+00
## Cumulative Proportion  0.94913 0.96780 0.98395 0.99500 1.0000 1.000e+00

4.1 Varianza explicada los componentes

varianza_explicada <- pca_result$sdev^2 /sum(pca_result$sdev^2)
varianza_acumulada <- cumsum(varianza_explicada)

df_varianza <- data.frame(
  Componente = 1:length(varianza_acumulada),
  Varianza_Acumulada = varianza_acumulada
)

ggplot(df_varianza, aes(x = Componente, y = Varianza_Acumulada)) +
  geom_line(color = "#0072B2", linewidth = 1) +
  geom_point(color = "#0072B2", size = 2) +
  geom_hline(yintercept = 0.80, linetype = "dashed", color = "red") +
  theme_minimal() +
  labs(title = "Varianza explicada acumulada",
       x = "Número de Componentes",
       y = "Varianza Acumulada") +
  scale_x_continuous(breaks = df_varianza$Componente)

Los resultados indican que el primer componente principal (PC1) captura una porción significativa de la varianza total, alrededor del 20%. Además, los primeros diez componentes en conjunto explican cerca del 70–75% de la variabilidad del conjunto de datos.

4.2 ¿Qué información nos dan las dos primeras componentes?

# Extraer scores de las dos primeras componentes
scores <- as.data.frame(pca_result$x[,1:2])
scores$country <- data$country

ggplot() +
  geom_point(data = scores, aes(x = PC1, y = PC2, color =
                                  country), alpha = 0.6) +
  theme_minimal() +
  labs(title = "Primeras dos componentes del PCA",
       x = "PC1",
       y = "PC2",
       color = "País")

Se representaron gráficamente las dos primeras componentes principales (PC1 y PC2) obtenidas del análisis PCA con el objetivo de visualizar cómo se distribuyen los países en el nuevo espacio de menor dimensión. En este gráfico, cada punto corresponde a una vivienda y los colores indican el país al que pertenece. En este caso, no se aprecia una separación fuerte entre países (quizás Singapur sí difiera del resto) lo que sugiere que las características cuantitativas consideradas presentan estructuras similares entre regiones, o que las diferencias entre países quedan capturadas en componentes posteriores con menor varianza explicada. En otras palabras, el país por sí solo no parece ser el factor principal de variación en las características numéricas de los inmuebles dentro del conjunto de datos.

4.3 Variables que más contribuyen a PC1 y PC2

# Qué variables contribuyen a PC1 y PC2
loadings <- as.data.frame(pca_result$rotation[,1:2])
loadings$variable <- rownames(loadings)

# Contribución para PC1
PC1_contrib_df <- loadings %>%
  select(PC1) %>%
  arrange(desc(PC1))

# Contribución para PC2
PC2_contrib_df <- loadings %>%
  select(PC2) %>%
  arrange(desc(PC2))

head(PC1_contrib_df, 5)
##                          PC1
## price              0.4666070
## log_price          0.4553414
## loan_amount        0.4365345
## property_size_sqft 0.4033568
## down_payment       0.3986256
head(PC2_contrib_df, 5)
##                              PC2
## rooms                0.705867249
## bathrooms            0.705777174
## emi_to_income_ratio  0.037967025
## property_size_sqft   0.009998963
## crime_cases_reported 0.009534732

PC1 está fuertemente asociado con el nivel económico y el tamaño de la propiedad. Las variables financieras (precio, cantidad de préstamo, pago inicial) y el tamaño del inmueble tienen cargas altas y positivas, lo que indica que este componente captura principalmente un gradiente de propiedades de mayor costo y mayor superficie frente a propiedades más económicas y pequeñas.

PC2 está dominado casi exclusivamente por la estructura interna del inmueble (número de habitaciones y baños). Estos dos atributos se mueven prácticamente de forma conjunta y definen la configuración habitacional del inmueble, independientemente del precio total.

5. Machine Learning: Modelos para predecir decision

5.1 Regresión logística con Lasso y Cross-Validation

data$decision <- as.factor(data$decision)
data$country <- as.factor(data$country)

train_index <- createDataPartition(data$decision, p = 0.8, list = FALSE)

train_data <- data[train_index, ]
test_data <- data[-train_index, ]

# Comprobar proporciones
prop.table(table(train_data$decision))
## 
##         0         1 
## 0.7696577 0.2303423
prop.table(table(test_data$decision))
## 
##         0         1 
## 0.7696692 0.2303308

Primero se convirtió la variable decision y country en factores, ya que ambas representan categorías y no valores numéricos continuos. A continuación, se realizó una partición del conjunto de datos en dos subconjuntos: uno de entrenamiento (80% de las observaciones) y otro de prueba (20% restante). La partición se llevó a cabo de manera estratificada utilizando la función createDataPartition, lo que garantiza que la proporción de clases en la variable decision se mantenga similar en ambos conjuntos. El conjunto de entrenamiento (train_data) se empleará para ajustar los modelos de clasificación, mientras que el conjunto de prueba (test_data) se utilizará posteriormente para evaluar su rendimiento y capacidad de generalización. Esto permite evitar el sobreajuste y obtener una estimación más realista del desempeño del modelo sobre datos no vistos durante el entrenamiento.

# Definir la fórmula del modelo
formula <- decision ~ log_price + country + satisfaction_score + bathrooms + rooms + previous_owners

x_train <- model.matrix(formula, data = train_data)[, -1]
y_train <- train_data$decision

x_test <- model.matrix(formula, data = test_data)[, -1]
y_test <- test_data$decision

En este bloque de código se define primero la fórmula del modelo, indicando que la variable respuesta será decision, mientras que las variables predictoras seleccionadas son log_price, country, satisfaction_score, bathrooms, rooms y previous_owners. A continuación, se utiliza la función model.matrix() para transformar esta fórmula en matrices numéricas que puedan ser utilizadas por modelos como Lasso o cualquier algoritmo de machine learning que requiera datos puramente numéricos. Esta función convierte automáticamente las variables categóricas, como country, en variables dummy (codificación one-hot), y posteriormente se elimina la primera columna para evitar problemas de multicolinealidad. De este modo, x_train y x_test contienen únicamente predictores numéricos, mientras que y_train y y_test guardan la variable objetivo en formato factor.

lasso_model_cv <- cv.glmnet(
  x_train,
  y_train,
  alpha = 1,
  family = "binomial",
  type.measure = "auc"
)

En este paso se entrena un modelo Lasso aplicado a regresión logística utilizando la función cv.glmnet(). El parámetro alpha = 1 especifica que se emplea la penalización Lasso, cuya característica principal es la selección automática de variables, ya que fuerza a algunos coeficientes a volverse exactamente cero, eliminando predictores poco relevantes. Se indica además family = “binomial” porque la variable respuesta decision es categórica binaria, por lo que el modelo ajusta probabilidades mediante una función logística. La opción type.measure = “auc” hace que el proceso de validación cruzada seleccione el valor óptimo del parámetro de regularización λ maximizando el área bajo la curva ROC, favoreciendo un modelo con buen poder discriminativo. De esta manera, el modelo resultante equilibra adecuadamente complejidad y rendimiento, reteniendo únicamente las variables más informativas para la predicción de la decisión de compra.

# Extraer el valor óptimo de Lambda (el que maximiza el AUC)
lambda_min <- lasso_model_cv$lambda.min

predictions_prob <- predict(lasso_model_cv, s = lambda_min,
                            newx = x_test, type = "response")[,1]

# Calcular la curva ROC y el AUC
roc_curve <- roc(response = y_test, predictor = predictions_prob)
auc_value <- auc(roc_curve)

coef_matrix <- coef(lasso_model_cv, s = lambda_min)

coef_plot <- data.frame(
  term = coef_matrix@Dimnames[[1]][coef_matrix@i + 1],
  estimate = coef_matrix@x)

# Filtrar el intercept y los coeficientes que Lasso hizo 0
coef_plot_filtered <- coef_plot %>%
  filter(term != "(Intercept)" & estimate != 0) %>%
  mutate(term = reorder(term, estimate))

Una vez entrenado el modelo Lasso mediante validación cruzada, se extrae el valor óptimo del parámetro de regularización λ (lambda.min), que corresponde al modelo con mayor área bajo la curva ROC (AUC). Utilizando este valor, se generan predicciones de probabilidad para los datos de test. Dichas probabilidades se evalúan mediante la curva ROC, obteniendo el valor del AUC, que resume la capacidad del modelo para discriminar entre compradores y no compradores.

A continuación, se extraen los coeficientes del modelo en el punto lambda.min. Debido a la penalización Lasso, algunos coeficientes se reducen a cero y, por tanto, esas variables se eliminan del modelo. Solo se conservan aquellas que presentan una contribución relevante a la predicción. Ahora se creará un gráfico que muestra únicamente los coeficientes distintos de cero, permitiendo visualizar el sentido y magnitud del efecto de cada variable. Coeficientes positivos indican que un aumento en dicha variable incrementa la probabilidad de compra, mientras que coeficientes negativos la reducen. Este enfoque facilita interpretar cuáles factores son más determinantes en la decisión y refuerza la capacidad del Lasso para realizar selección de variables de manera directa y eficiente.

ggplot(coef_plot_filtered, aes(x = term, y = estimate)) +
  geom_point(aes(color = estimate > 0), size = 3) +
  geom_segment(aes(x = term, xend = term, y = 0, yend = estimate)) +
  coord_flip() +
  theme_minimal() +
  scale_color_discrete(name = "Efecto", labels = c("Negativo", "Positivo")) +
  labs(title = "Efecto de las variables seleccionadas por Lasso",
       x = "Variables", 
       y = "Coeficiente (Log-Odds)")

El modelo de regresión logística muestra que la decisión de compra está influida principalmente por el precio de la vivienda (en su escala logarítmica), el país donde se ubica el inmueble y el nivel de satisfacción del comprador. En particular, se observa que el coeficiente de log_price es negativo y altamente significativo (p-value < 2e-16), lo que indica que, a medida que el precio aumenta, la probabilidad de que el comprador decida adquirir la vivienda disminuye. Este resultado sugiere que la capacidad económica es un factor limitante importante en la decisión.

La variable satisfaction_score presenta un efecto positivo y muy significativo, implicando que un mayor nivel de satisfacción incrementa considerablemente la probabilidad de compra. Este resultado destaca el papel de la percepción personal y la valoración emocional del inmueble en la toma de decisiones, además de los factores económicos.

En cuanto al país de residencia, se identifican diferencias significativas entre mercados. Los compradores en Brasil, India, Sudáfrica, Singapur, Emiratos Árabes Unidos y Estados Unidos presentan probabilidades de compra significativamente menores en comparación con el país de referencia (Australia).

Por el contrario, variables relacionadas con las características físicas del inmueble —como el número de baños (bathrooms), de habitaciones (rooms) o de propietarios anteriores (previous_owners)— no muestran efectos estadísticamente significativos cuando se controlan las demás variables del modelo.

5.2 Random Forest con GridSearch y Cross-Validation

Vamos a seleccionar las variables relevantes y a preparar los datos de igual manera que para la regresión logística.

ml_data <- data %>%
  select(decision, log_price, country, satisfaction_score,
         neighbourhood_rating, connectivity_score,
         emi_to_income_ratio, bathrooms, rooms,
         previous_owners)

ml_data$decision <- as.factor(ml_data$decision)
ml_data$country <- as.factor(ml_data$country)

# Dividir en train y test
train_index <- createDataPartition(ml_data$decision, p = 0.8, list = FALSE)
train <- ml_data[train_index, ]
test  <- ml_data[-train_index, ]

# Comprobar proporciones
prop.table(table(train$decision))
## 
##         0         1 
## 0.7696577 0.2303423
prop.table(table(test$decision))
## 
##         0         1 
## 0.7696692 0.2303308

A continuación, creamos y entrenamos nuestro modelo.

train$decision <- factor(train$decision, levels = c(0,1), labels = c("No","Si"))
test$decision  <- factor(test$decision,  levels = c(0,1), labels = c("No","Si"))

dummies <- dummyVars(decision ~ ., data = train)
train_x <- predict(dummies, newdata = train)
test_x  <- predict(dummies, newdata = test)

train_y <- ifelse(train$decision == "Si", 1, 0)
test_y  <- ifelse(test$decision == "Si", 1, 0)

dtrain <- xgb.DMatrix(data = as.matrix(train_x), label = train_y)
dtest  <- xgb.DMatrix(data = as.matrix(test_x), label = test_y)
ctrl <- trainControl(
  method = "cv",
  number = 5,
  classProbs = TRUE,
  summaryFunction = twoClassSummary,
  verboseIter = TRUE
)

grid <- expand.grid(
  nrounds = seq(100, 500, by = 100),
  max_depth = c(3, 5, 7),
  eta = c(0.01, 0.05, 0.1),
  gamma = 0,
  colsample_bytree = c(0.6, 0.8),
  min_child_weight = c(1, 3),
  subsample = c(0.7, 1)
)

xgb_model <- train(
  x = train_x,
  y = train$decision,
  method = "xgbTree",
  metric = "ROC",
  trControl = ctrl,
  tuneGrid = grid
)

En este bloque se define y entrena un modelo de clasificación basado en XGBoost utilizando validación cruzada y ajuste de hiperparámetros. La función trainControl() establece una validación cruzada de 5 particiones, calculando probabilidades de clase y empleando el AUC como métrica principal mediante summaryFunction = twoClassSummary, lo que permite evaluar la capacidad discriminativa del modelo. Posteriormente, se construye una rejilla de hiperparámetros que incluye diferentes combinaciones de parámetros. El objetivo de esta búsqueda es encontrar la configuración que maximiza el rendimiento predictivo del modelo evitando sobreajuste.

pred_prob <- predict(xgb_model, newdata = test_x, type = "prob")[, "Si"]
pred_class <- ifelse(pred_prob > 0.5, "Si", "No")

plot_confusion <- function(actual, predicted, title = "Matriz de Confusión") {
  
  # Matriz de confusión
  cm <- confusionMatrix(factor(predicted), factor(actual))
  
  # Convertir a tabla para ggplot
  cm_table <- as.data.frame(cm$table)
  colnames(cm_table) <- c("Predicción", "Real", "Freq")
  
  ggplot(cm_table, aes(x = Real, y = Predicción, fill = Freq)) +
    geom_tile(color = "white") +
    geom_text(aes(label = Freq), size = 6, color = "black") +
    scale_fill_gradient(low = "#D6EAF8", high = "#1B4F72") +
    theme_minimal() +
    labs(title = title, fill = "Frecuencia") +
    theme(
      plot.title = element_text(face = "bold", size = 14),
      axis.title.x = element_text(face = "bold"),
      axis.title.y = element_text(face = "bold")
    )
}

plot_confusion(test$decision, factor(pred_class), "Resultados en Test Set")

En esta sección se evalúa el desempeño del modelo utilizando el conjunto de test. Primero, se generan probabilidades de pertenencia a la clase “Si” y luego se convierten en predicciones binarias aplicando un umbral de 0.5. Para interpretar los resultados, se construye una matriz de confusión que compara las predicciones con los valores reales. La función plot_confusion() transforma la matriz en un formato visual mediante ggplot2, donde los recuadros indican la cantidad de observaciones clasificadas en cada categoría y la intensidad del color refleja la frecuencia.

roc_obj <- roc(test_y, pred_prob)

roc_df <- data.frame(
  specificity = roc_obj$specificities,
  sensitivity = roc_obj$sensitivities
)

ggplot(roc_df, aes(x = 1 - specificity, y = sensitivity)) +
  geom_line(size = 1.2, color = "#1c61b6") +
  geom_abline(linetype = "dashed", color = "gray50") +
  theme_minimal() +
  labs(title = paste("Curva ROC - XGBoost (AUC =", round(auc(roc_obj), 3), ")"),
       x = "1 - Especificidad",
       y = "Sensibilidad")

La curva ROC representa la capacidad del modelo para distinguir entre compradores que toman la decisión de compra (“Si”) y aquellos que no (“No”). En el eje vertical se muestra la sensibilidad, mientras que en el eje horizontal se representa 1−especificidad. La línea diagonal punteada corresponde al desempeño de un clasificador aleatorio, por lo que cuanto más alejada esté la curva de esa diagonal y más cerca se encuentre de la esquina superior izquierda, mejor es el desempeño del modelo. El valor del AUC obtenido (AUC = 0.95) indica que el modelo presenta una capacidad predictiva buena, siendo capaz de discriminar entre ambas clases con un nivel de precisión considerable.

importance <- xgb.importance(model = xgb_model$finalModel) %>%
  mutate(Feature = reorder(Feature, Gain))

ggplot(importance, aes(x = Feature, y = Gain)) +
  geom_col(fill = "#0072B2") +
  coord_flip() +
  theme_minimal() +
  labs(title = "Importancia de las Variables - XGBoost",
       x = "Variable",
       y = "Ganancia (Gain)")

El gráfico de importancia de variables muestra cuáles características aportan más a las decisiones del modelo. La métrica utilizada es Gain, que representa cuánto mejora la capacidad predictiva del modelo cada variable cuando se usa en los árboles. Las variables situadas en la parte superior del gráfico tienen una contribución mayor, por lo que influyen más en la predicción de la decisión de compra. En cambio, las variables con valores de ganancia menores aportan menos información y tienen un impacto reducido en el resultado final. Este análisis permite identificar que tanto la satisfacción como el ratio emi/salario son más relevantes para explicar el comportamiento de compra.

6. Interpretación de resultados

El análisis exploratorio inicial permitió identificar que el precio de la vivienda presenta una distribución fuertemente asimétrica hacia la derecha, lo que sugiere la presencia de propiedades de alto valor que elevan significativamente la media. Para abordar esta asimetría, se aplicó una transformación logarítmica al precio, consiguiendo una distribución más cercana a la normalidad y adecuada para los modelos posteriores.

No obstante, aunque la prueba de Shapiro–Wilk indicó que la normalidad no se cumple estrictamente, el tamaño muestral elevado permite confiar en la robustez de los contrastes aplicados. Asimismo, el test de Levene mostró que no existen diferencias estadísticamente significativas en la varianza del precio entre países, lo que justificó el uso de un ANOVA clásico para comparar medias entre regiones.

En cuanto al modelo de regresión logística, los resultados muestran que la decisión de compra está influida principalmente por el precio de la propiedad, el país y el nivel de satisfacción del comprador. El coeficiente negativo y altamente significativo asociado a log_price indica que, a medida que el precio de la vivienda aumenta, disminuye la probabilidad de compra. Esto sugiere que, incluso en mercados con abundante financiación, el coste continúa siendo un factor determinante en la viabilidad de la adquisición.

Por otro lado, el coeficiente positivo de satisfaction_score revela que la valoración subjetiva del inmueble tiene un peso importante en la decisión, lo que resalta el componente emocional y experiencial del proceso de compra. Además, el análisis por país evidenció que algunos mercados, como Brasil, India, Sudáfrica, Singapur, UAE y Estados Unidos, presentan menores probabilidades de compra, lo que puede estar relacionado con diferencias económicas, culturales o estructurales en los mercados inmobiliarios considerados.

El análisis de componentes principales (PCA) se utilizó para reducir la dimensionalidad del conjunto de datos y detectar patrones en las variables relacionadas con la propiedad, el comprador y la financiación. Los resultados mostraron que el primer componente principal (PC1) explica alrededor del 20% de la variabilidad total, mientras que las diez primeras componentes capturan aproximadamente entre el 70% y el 75% de la varianza.

Esto indica que, aunque la información está relativamente dispersa, existen combinaciones lineales de las variables que permiten sintetizar parte relevante del comportamiento del sistema. La visualización de los individuos en el espacio de las dos primeras componentes reveló algunas leves diferencias en la distribución de los datos según el país, lo cual sugiere patrones estructurales en los mercados inmobiliarios analizados.

7. Conclusiones

Los resultados obtenidos en el estudio permiten concluir que la decisión de compra de una vivienda no depende únicamente de factores económicos objetivos como el precio o las condiciones de financiación, sino también de variables subjetivas como la satisfacción percibida con el inmueble. Aunque el aumento del precio reduce significativamente la probabilidad de compra, un mayor nivel de satisfacción puede contrarrestar parcialmente este efecto, explicando por qué viviendas aparentemente comparables pueden obtener resultados de venta diferentes. Asimismo, la influencia significativa del país pone de manifiesto la heterogeneidad del mercado inmobiliario global, donde las decisiones y comportamientos financieros no pueden generalizarse entre regiones.

El PCA permitió sintetizar parte de la variabilidad presente en el conjunto de datos y sugirió la existencia de estructuras relacionadas tanto con el perfil financiero del comprador (variables que contribuyen principalmente a PC1) como con las características de la vivienda (variables que contribuyen principalmente a PC2). En conjunto, los análisis realizados refuerzan la importancia de integrar factores económicos, sociales y emocionales en la interpretación de comportamientos de compra dentro del sector inmobiliario.

8. Limitaciones

Este estudio presenta algunas limitaciones que deben considerarse al interpretar los resultados. En primer lugar, aunque el dataset es amplio, se conoce que estos datos han sido generados de manera sintética, lo que implica que las relaciones observadas pueden no reflejar con exactitud el comportamiento de un mercado inmobiliario real.

En segundo lugar, el modelo de regresión logística captura relaciones lineales entre las variables, pero la toma de decisiones en la compra de vivienda puede involucrar efectos no lineales y dinámicas que no han sido modeladas.

Finalmente, si bien el PCA ha permitido explorar patrones globales, su interpretación puede ser limitada y sería conveniente complementarlo con análisis adicionales como el clustering.

9. Bibliografía

[1] Kaggle (2025). Global House Purchase Decision Dataset. Obtenido de: https://www.kaggle.com

[2] OpenAI (2025). Asistencia en redacción y análisis de datos mediante ChatGPT. Herramienta empleada para la interpretación estadística, síntesis de resultados y redacción del informe.